package com.gxgeek.redis.application.lock;
//import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;

import java.util.Random;

/**
 * Created by gx on 2017/7/10.
 */
public class RedisLock {
    //加锁标志
    public static final String LOCKED = "TRUE";
    public static final long ONE_MILLI_NANOS = 1000000L;
    //默认超时时间（毫秒）
    public static final long DEFAULT_TIME_OUT = 3000;
    public static JedisPool pool;
    public static final Random r = new Random();
    //锁的超时时间（秒），过期删除
    public static final int EXPIRE = 5 * 60;

    static {
        pool = new JedisPool(new JedisPoolConfig(), "host", 6379);
    }

    private Jedis jedis;
    private String key;
    //锁状态标志
    private boolean locked = false;

    public RedisLock() {
    }

//    public RedisLock(String key) {
//        this.key = key;
//        this.jedis = pool.getResource();
//    }

//    @Test
    public boolean lock(long timeout) {
        long nano = System.nanoTime();
        timeout *= ONE_MILLI_NANOS;
        try {
            while ((System.nanoTime() - nano) < timeout) {
                if (jedis.setnx(key, LOCKED) == 1) {
                    jedis.expire(key, EXPIRE);
                    locked = true;
                    return locked;
                }
                // 短暂休眠，nano避免出现活锁
                Thread.sleep(3, r.nextInt(500));
            }
        } catch (Exception e) {
        }
        return false;
    }

    public boolean lock() {
        return lock(DEFAULT_TIME_OUT);
    }

    // 无论是否加锁成功，必须调用
    public void unlock() {
        try {
            if (locked)
                jedis.del(key);
        } finally {
            jedis.close();
//            pool.returnResource(jedis);
        }
    }

//2. 通过事务(multi)实现
//    @Test
    public boolean lock_2(long timeout) {
        long nano = System.nanoTime();
        timeout *= ONE_MILLI_NANOS;
        try {
            while ((System.nanoTime() - nano) < timeout) {
                Transaction t = jedis.multi();
                // 开启事务，当server端收到multi指令
                // 会将该client的命令放入一个队列，然后依次执行，知道收到exec指令
                t.getSet(key, LOCKED);
                t.expire(key, EXPIRE);
                String ret = (String) t.exec().get(0);
                if (ret == null || ret.equals("UNLOCK")) {
                    return true;
                }
                // 短暂休眠，nano避免出现活锁
                Thread.sleep(3, r.nextInt(500));
            }
        } catch (Exception e) {
        }
        return false;
    }



//    3. 通过事务+监听实现
//    @Test
    public boolean lock_3(long timeout) {
        long nano = System.nanoTime();
        timeout *= ONE_MILLI_NANOS;
        try {
            while ((System.nanoTime() - nano) < timeout) {
                jedis.watch(key);
                // 开启watch之后，如果key的值被修改，则事务失败，exec方法返回null
                String value = jedis.get(key);
                if (value == null || value.equals("UNLOCK")) {
                    Transaction t = jedis.multi();
                    t.setex(key, EXPIRE, LOCKED);
                    if (t.exec() != null) {
                        return true;
                    }
                }
                jedis.unwatch();
                // 短暂休眠，nano避免出现活锁
                Thread.sleep(3, r.nextInt(500));
            }
        } catch (Exception e) {
        }
        return false;
    }

}